﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Kira.Compiler.Bods
{
    [XmlElementMapping("DIQuery")]
    public class DIQuery : BodsTransform
    {
        public String OutputName
        {
            get
            {
                return this.GetElement("DISchema").GetAttrbuteValue("name");
            }
        }

        public IEnumerable<DIElement> Columns
        {
            get
            {
                return this.GetElement("DISchema").GetElements<DIElement>();
            }
        }

        public bool IsDistinct
        {
            get
            {
                return this.GetElement("DISelect").HasAttribute("isDistinct") ? bool.Parse(this.GetElement("DISelect").GetAttrbuteValue("isDistinct")) : false;
            }
        }

        public IEnumerable<BodsExpression> ColumnExpressions
        {
            get
            {
                return from c in this.GetElement("DISelect").GetElement("DIProjection").GetElements("DIExpression") select c.GetElement<BodsExpression>();
            }
        }

        public bool HasWhereExpression
        {
            get
            {
                return this.GetElement("DISelect").HasElement("DIWhere");
            }
        }

        public BodsExpression WhereExpression
        {
            get
            {
                return this.GetElement("DISelect").GetElement("DIWhere").GetElement("DIExpression").GetElement<BodsExpression>();
            }
        }

        public bool HasOrderbys
        {
            get
            {
                return this.GetElement("DISelect").HasElement("DIOrderBys");
            }
        }

        public IEnumerable<DIOrderBy> OrderBys
        {
            get
            {
                return this.GetElement("DISelect").GetElement("DIOrderBys").GetElements<DIOrderBy>();
            }
        }

        public bool HasGroupByExpressions
        {
            get
            {
                return this.GetElement("DISelect").HasElement("DIGroupBy");
            }
        }

        public IEnumerable<BodsExpression> GroupByExpressions
        {
            get
            {
                return this.GetElement("DISelect").GetElement("DIGroupBy").GetElements<BodsExpression>();
            }
        }

        public bool HasJoins
        {
            get
            {
                return this.GetElement("DISelect").HasElement("DILeftOuterJoin");
            }
        }

        public IEnumerable<DILeftOuterJoinPair> Joins
        {
            get
            {
                return this.GetElement("DISelect").GetElement("DILeftOuterJoin").GetElements<DILeftOuterJoinPair>();
            }
        }

        public override IEnumerable<String> InputNames
        {
            get
            {
                return from c in this.GetElement("DISelect").GetElement("DIFrom").GetElements("DITableSpec") select c.GetAttrbuteValue("name");
            }
        }

        public override IEnumerable<string> OutputNames
        {
            get { return new String[] { this.OutputName }; }
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(GetComments("DIQuery"));
            sb.Append("WITH ").Append(OutputName).AppendLine(" AS (");
            //select list
            List<string> selects = new List<string>();
            var it = ColumnExpressions.GetEnumerator();
            foreach (var alias in Columns)
            {
                bool hasExp = it.MoveNext();
                var exp = it.Current;

                if (hasExp)
                {
                    selects.Add(exp.ToString() + " AS " + alias.Name);
                }
                else
                {
                    selects.Add(alias.Name + " AS " + alias.Name);
                }
            }
            sb.Append(selects.Aggregate((a, b) => a + ",\n" + b));
            sb.AppendLine();
            sb.Append("FROM ");
            //join operation, actually just list all tables, because bods doesn't have a real join-clause
            sb.AppendLine(InputNames.Aggregate((a, b) => a + ", " + b));
            //where
            if (HasWhereExpression)
            {
                sb.Append("WHERE ").AppendLine(WhereExpression.ToString());
            }
            //group by
            if (HasGroupByExpressions)
            {
                sb.Append("GROUP BY ").AppendLine(GroupByExpressions.ToString());
            }
            //order by
            if (HasOrderbys)
            {
                sb.Append("ORDER BY ").AppendLine(OrderBys.Select(c => c.Expression.ToString() + " " + (c.IsAscending ? "ASC" : "DESC")).Aggregate((a, b) => a + ", " + b));
            }
            sb.AppendLine(")");
            return sb.ToString();
        }
    }

    [XmlElementMapping("DIOrderBy")]
    public class DIOrderBy : BodsElementBase
    {
        public bool IsAscending
        {
            get
            {
                return this.GetAttrbuteValue("direction") == "ASC";
            }
        }

        public BodsExpression Expression
        {
            get
            {
                return this.FirstElement.GetElement<BodsExpression>();
            }
        }
    }

    [XmlElementMapping("DILeftOuterJoinPair")]
    public class DILeftOuterJoinPair : BodsElementBase
    {
        public String OuterTableName
        {
            get
            {
                return this.GetAttrbuteValue("outer");
            }
        }

        public String InnerTableName
        {
            get
            {
                return this.GetAttrbuteValue("inner");
            }
        }
    }


}
